//
//  Sudoku.swift
//  APSwift
//
//  Created by jianbo.pei on 2023/10/7.
//

/**
 **九宫格**，在项目开发中，时常遇到，有时候选择用`UICollectionView`处理，但有时候又觉得没必要，就想纯粹的用`for`循环处理！
    然而，在纯粹的用`for`循环处理时，经常会计算`item`的`x`、`y`、`width`而比比划划，
    故整理一下，外部只管设置相应配置，不需要考虑具体怎么计算！
 */

import Foundation
import UIKit

struct SudokuConfiguration {
    /// 九宫格的内边距
    var edge: UIEdgeInsets = .zero
    
    /// 九宫格父视图的宽
    var superWidth: CGFloat = UIScreen.main.bounds.width
    
    /// 九宫格水平方向（即：左右）item之间的间距
    var horizontalSpacing: CGFloat = 5
    
    /// 九宫格垂直方向（即：上下）item之间的间距
    var verticalSpacing: CGFloat = 5
    
    /// 九宫格一个item的高度
    var itemHeight: CGFloat = 40
    
    /// 九宫格总item数量
    var itemCount: Int = 9
    
    /// 九宫格的列数
    var column: Int = 3
    
    static var `default` = SudokuConfiguration()
    
    /// 九宫格一个item的宽
    var itemWidth: CGFloat {
        get {
            let totalSpacing = CGFloat(column - 1) * horizontalSpacing
            let iw = (superWidth - totalSpacing - edge.left - edge.right) / CGFloat(column)
            return iw
        }
    }
    
    /// 获取九宫格某个index下的item的x
    /// - Parameters:
    ///   - index: 九宫格当前item的坐标
    ///   - itemW: 九宫格item宽（这里要求传入，是为了减少不必要的`itemWidth`计算）
    /// - Returns: 当前item的x
    func getItemX(at index: Int, itemW: CGFloat) -> CGFloat {
        let x = CGFloat(index % column) * (itemW + horizontalSpacing)
        return x + edge.left
    }
    
    /// 获取九宫格某个index下的item的y
    /// - Parameters:
    ///   - index: 九宫格当前item的坐标
    ///   - itemW: 九宫格item宽（这里要求传入，是为了减少不必要的`itemWidth`计算）
    /// - Returns: 当前item的y
    func getItemY(at index: Int) -> CGFloat {
        let y = CGFloat(index / column) * (itemHeight + verticalSpacing)
        return y + edge.top
    }
    
    /// 计算九宫格的高
    /// - Returns: 九宫格的高
    func calculateHeight() -> CGFloat {
        var totalHeight = 0.0
        if itemCount % column == 0 {
            /// 刚好一整排（0、3、6、9等）
            totalHeight = CGFloat(itemCount / column) * (itemHeight + verticalSpacing)
        } else {
            /// 不是整排（2：2/3=0，但是也是1排，所以加1）
            totalHeight = CGFloat(itemCount / column + 1) * (itemHeight + verticalSpacing)
        }
        return totalHeight
    }
}

struct Sudoku {
    /// 九宫格布局
    /// - Parameters:
    ///   - config: `SudokuConfiguration`实例回调
    ///   - completion: item的frame和坐标计算完毕回调
    static func layout(config: ((inout SudokuConfiguration) -> ())? = nil, completion: (CGRect, Int) -> ()) {
        var configuration = SudokuConfiguration.default
        if let conf = config {
            conf(&configuration)
        }
        layout(configuration, completion: completion)
    }
    
    /// 九宫格布局
    /// - Parameters:
    ///   - configuration: 自定义的`SudokuConfiguration`实例
    ///   - completion: item的frame和坐标计算完毕回调
    static func layout(_ configuration: SudokuConfiguration, completion: (CGRect, Int) -> ()) {
        let itemW = configuration.itemWidth
        for idx in 0..<configuration.itemCount {
            let itemX = configuration.getItemX(at: idx, itemW: itemW)
            let itemY = configuration.getItemY(at: idx)
            completion(CGRect(x: itemX, y: itemY, width: itemW, height: configuration.itemHeight), idx)
        }
    }
    
    /// 计算九宫格的高
    /// - Parameter configuration: 自定义的`SudokuConfiguration`实例
    /// - Returns: 九宫格的高
    static func calculateHeight(_ configuration: SudokuConfiguration) -> CGFloat {
        return configuration.calculateHeight()
    }
}
